/****************************************************************************

 Copyright (C) 2009 Jean Pierre Charalambos. All rights reserved.

 Jean Pierre Charalambos (http://dis.unal.edu.co/profesores/pierre/) is an
 Affiliate Associate Professor of the Faculty of Engineering at National
 University of Colombia (http://www.unal.edu.co/).

 This file is part of maku version 0.1.

 http://code.google.com/p/maku/
 email: jpcharalambosh@unal.edu.co

 This file may be used under the terms of the GNU General Public License 
 versions 2.0 or 3.0 as published by the Free Software Foundation and
 appearing in the COPYING file included in the packaging of this file.

 This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

*****************************************************************************/

#include "viewer.h"

#if !defined(M_PI)
# define M_PI 3.1415926535897931
#endif

void Viewer::initViewer() {
  //connections:
  if(scene!=0)
    connectScene();

  setAxisIsDrawn();
  setGridIsDrawn();

  setFocusPolicy(StrongFocus);

  setAcceptDrops( true );
  setCursor(Qt::PointingHandCursor);

  //keyboard:
  setKeyDescription(Key_P, "Fires the performance test");
  setKeyDescription(Key_E, "Toggles camera mode");
  setKeyDescription(Key_O, "Enable/Disable fog");
  setKeyDescription(Key_K, "Enable/Disable back face culling");
  setKeyDescription(Key_I, "Display information");
  //setShortcut(SAVE_SCREENSHOT, 0); //disables
  setShortcut(SAVE_SCREENSHOT, Qt::CTRL+Qt::Key_H);
}

void Viewer::draw() {
  makeCurrent();
  scene->draw();
  if (scene->drawBB) scene->getBoundingBox().draw();
  if (scene->drawBS) scene->getBoundingSphere().draw();
}

/** To make beginners' life easier and tvirtual void draw();o simplify the examples, initializeGL() slightly modifies the standard OpenGL state:
 glEnable(GL_LIGHT0);
 glEnable(GL_LIGHTING);
 glEnable(GL_DEPTH_TEST);
 glEnable(GL_COLOR_MATERIAL);
*/
void Viewer::init() {
  makeCurrent();
  //std OpenGL:

  //glDisable(GL_DEPTH_TEST);
  //glEbable(GL_DEPTH_TEST);
  glDisable(GL_LIGHT0);
  glDisable(GL_LIGHTING);
  glDisable(GL_COLOR_MATERIAL);
  setBackgroundColor( Qt::black );
  //material
  scene->setDefaultMaterial(); //no funciona si glEnable(GL_COLOR_MATERIAL);
  // Lighting
  setDefaultLight();
  setFog();
  //restoreStateFromFile();
}

double Viewer::performance() {
    double fps(0.0);
    setCursor( Qt::WaitCursor );
    QTime timer(0, 0, 0, 0);
    unsigned int  i;
    unsigned int  frames = 60;
    const float   angle  = 2*M_PI/(float)frames;
    qglviewer::Vec        x_axis(1,0,0), y_axis(0,1,0), z_axis(0,0,1);
    qglviewer::Quaternion qx(x_axis, angle), qy(y_axis, angle), qz(z_axis, angle);

    timer.start();
    for (i=0; i<frames; ++i) {
      camera()->frame()->rotateAroundPoint(qx, sceneCenter());
      paintGL();
      swapBuffers();
    }
    for (i=0; i<frames; ++i) {
      camera()->frame()->rotateAroundPoint(qy, sceneCenter());
      paintGL();
      swapBuffers();
    }
    for (i=0; i<frames; ++i) {
      camera()->frame()->rotateAroundPoint(qz, sceneCenter());
      paintGL();
      swapBuffers();
    }
    fps =  (1000.0 * 3.0 * frames) / timer.elapsed();
    setCursor( Qt::PointingHandCursor );

    return fps;
}

void Viewer::keyPressEvent(QKeyEvent *e) {
    makeCurrent();
    const Qt::KeyboardModifiers modifiers = e->modifiers();
    bool handled = false;
    if ( (e->key()==Qt::Key_P) && (modifiers==Qt::NoButton) ) {
      double fps = performance();
      cout << "fps: " << fps << endl;
      handled = true;
    }
    else
    if ( (e->key()==Qt::Key_O) && (modifiers==Qt::NoButton) ) {
      if ( glIsEnabled( GL_FOG ) ) {
	glDisable( GL_FOG );
	std::cout << "Fog: disabled\n";
      }
      else       {
	glEnable( GL_FOG );
	std::cout << "Fog: enabled\n";
      }
      handled = true;
      updateGL();
    }
    else
    if ( (e->key()==Qt::Key_K) && (modifiers==Qt::NoButton) ) {
      if ( glIsEnabled( GL_CULL_FACE ) ) {
	glDisable( GL_CULL_FACE );
	std::cout << "Back face culling: disabled\n";
      }
      else {
	glEnable( GL_CULL_FACE );
	std::cout << "Back face culling: enabled\n";
      }
      handled = true;
      updateGL();
    }
    else
    if ( (e->key()==Qt::Key_I) && (modifiers==Qt::NoButton) ) {
      std::cout << "Scene radius: " << sceneRadius() << std::endl;
      std::cout << "Scene center: " << sceneCenter() << std::endl;
      handled = true;
    }
    else
    if ( (e->key()==Qt::Key_E) && (modifiers==Qt::NoButton) ) {
      if (camera()->type()==qglviewer::Camera::ORTHOGRAPHIC ){
          camera()->setType( qglviewer::Camera::PERSPECTIVE );
          std::cout << "Camera perspective mode set\n";
          }
      else {
          camera()->setType( qglviewer::Camera::ORTHOGRAPHIC );
          std::cout << "Camera ortho mode set\n";
          }
      emit cameraModeChanged();
      updateGL();
      handled = true;
    }
    if (!handled)
     QGLViewer::keyPressEvent(e);
}

void Viewer::adjustScene() {
  Vec m(vector_cast<Vec3f>(scene->getBBMin())[0],
        vector_cast<Vec3f>(scene->getBBMin())[1],
	vector_cast<Vec3f>(scene->getBBMin())[2]);
  Vec M(vector_cast<Vec3f>(scene->getBBMax())[0],
        vector_cast<Vec3f>(scene->getBBMax())[1],
	vector_cast<Vec3f>(scene->getBBMax())[2]);
  camera()->setPosition( Vec(0,0,1) );
  camera()->setUpVector( Vec(0,1,0) );
  camera()->lookAt( Vec(0,0,0) );
  setSceneBoundingBox(m, M);
  //fog
  glFogf( GL_FOG_START,      1.5*sceneRadius() );
  glFogf( GL_FOG_END,        3.0*sceneRadius() );
  showEntireScene();
}

void Viewer::setDefaultLight(void) {
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  GLfloat pos1[] = { 0.1,  0.1, -0.02, 0.0};
  GLfloat pos2[] = {-0.1,  0.1, -0.02, 0.0};
  GLfloat pos3[] = { 0.0,  0.0,  0.1,  0.0};
  GLfloat col1[] = { 0.7,  0.7,  0.8,  1.0};
  GLfloat col2[] = { 0.8,  0.7,  0.7,  1.0};
  GLfloat col3[] = { 1.0,  1.0,  1.0,  1.0};
 
  glEnable(GL_LIGHT0);
  glLightfv(GL_LIGHT0,GL_POSITION, pos1);
  glLightfv(GL_LIGHT0,GL_DIFFUSE,  col1);
  glLightfv(GL_LIGHT0,GL_SPECULAR, col1);
  
  glEnable(GL_LIGHT1);
  glLightfv(GL_LIGHT1,GL_POSITION, pos2);
  glLightfv(GL_LIGHT1,GL_DIFFUSE,  col2);
  glLightfv(GL_LIGHT1,GL_SPECULAR, col2);
  
  glEnable(GL_LIGHT2);
  glLightfv(GL_LIGHT2,GL_POSITION, pos3);
  glLightfv(GL_LIGHT2,GL_DIFFUSE,  col3);
  glLightfv(GL_LIGHT2,GL_SPECULAR, col3);
}

void Viewer::setFog() {
  GLfloat fogColor[4] = { 0.3, 0.3, 0.4, 1.0 };
  glFogi(GL_FOG_MODE,    GL_LINEAR);
  glFogfv(GL_FOG_COLOR,  fogColor);
  glFogf(GL_FOG_DENSITY, 0.35);
  glHint(GL_FOG_HINT,    GL_DONT_CARE);
  glFogf(GL_FOG_START,    5.0f);
  glFogf(GL_FOG_END,     25.0f);
}
